﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation.Runspaces;
using System.Reflection;
using NUnit.Framework;

namespace NUnit4PowerShell
{
    [TestFixture]
    public class PSFixture
    {
        /// <summary>
        /// PowerShell file path containing the PowerShell test function to execute
        /// </summary>
        private readonly string _psFixtureFilePath = PSFixtureProvider.GetOne();
        private string PSFixtureFileName
        {
            get
            {
                return Path.GetFileNameWithoutExtension(_psFixtureFilePath);
            }
        }

        public PSFixture()
        {
            Logger.Instance.Info("[PSFixture] Fixture constructor", PSFixtureFileName);
            InitializePSFixture();
            Logger.Instance.AddEmptyLine();
        }

        #region Private Members

        #region Fixture name functions definition

        private const string SetUpFixtureFunctionName = "SetUp-TestFixture";
        private const string SetUpFunctionName = "SetUp-Test";
        private const string TearDownFunctionName = "TearDown-Test";
        private const string TearDownFixtureFunctionName = "TearDown-TestFixture";

        private bool _setUpFixtureFunctionExists;
        private bool _setUpFunctionExists;
        private bool _tearDownFunctionExists;
        private bool _tearDownFixtureFunctionExists;
        
        #endregion

        /// <summary>
        /// Common runspace for the PowerShell commands execution
        /// </summary>
        private Runspace _psRunspace;

        /// <summary>
        /// Name pattern of functions to test in the PowerShell file
        /// </summary>
        private static readonly string PSTestFunctionNamePattern = Utils.GetPSTestFunctionNamePattern();

        #endregion

        #region Properties

        /// <summary>
        /// This list contains all the PowerShell functions to test inside this fixture and executed by the TestCaseSource
        /// </summary>
        private static IList<string> _psTestList;

        /// <summary>
        /// This list contains all the PowerShell functions to test inside this fixture and executed by the TestCaseSource
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> PSTestList()
        {
            if (_psTestList == null)
            {
                throw new NullReferenceException("[FunctionsToTest] Functions list to test is null");
            }
            return _psTestList;
        }

        #endregion

        private void InitializePSFixture()
        {
            Uri u = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string dir = System.IO.Path.GetDirectoryName(u.LocalPath);
            System.Environment.CurrentDirectory = dir;

            Logger.Instance.Info("[InitializePSFixture] Current directory is : " + dir, PSFixtureFileName);

            using (Runspace runspace = PSUtils.PSRunspaceSetup(_psFixtureFilePath))
            {
                try
                {
                    // Get PowerShell functions used for TestFixtureSetUp, SetUp, TearDown and TestFixtureTearDown
                    _setUpFixtureFunctionExists = PSUtils.ExecutePSFixtureFunction(SetUpFixtureFunctionName, runspace,_psFixtureFilePath);
                    _setUpFunctionExists = PSUtils.ExecutePSFixtureFunction(SetUpFunctionName, runspace, _psFixtureFilePath);
                    _tearDownFunctionExists = PSUtils.ExecutePSFixtureFunction(TearDownFunctionName, runspace, _psFixtureFilePath);
                    _tearDownFixtureFunctionExists = PSUtils.ExecutePSFixtureFunction(TearDownFixtureFunctionName, runspace, _psFixtureFilePath);

                    // Get all functions to test in this fixture
                    _psTestList = PSUtils.FindPSTests(PSTestFunctionNamePattern, runspace, PSFixtureFileName);
                    Logger.Instance.Info("[InitializePSFixture] Count of function to test : " + _psTestList.Count, PSFixtureFileName);
                    foreach (string functionName in _psTestList)
                    {
                        Logger.Instance.Info("[InitializePSFixture] Functions to test" + functionName, PSFixtureFileName);
                    }

                }
                catch (Exception exception)
                {
                    Logger.Instance.Error("[InitializePSFixture]" + exception, PSFixtureFileName);
                    throw;
                }
                finally
                {
                    runspace.Close();
                }
            }
        }

        #region Fixture methods

        [TestFixtureSetUp]
        public void TestFixtureSetup()
        {
            Logger.Instance.Info("TestFixtureSetup", PSFixtureFileName);
            using (_psRunspace = PSUtils.PSRunspaceSetup(_psFixtureFilePath))
            {
                if (_setUpFixtureFunctionExists)
                {
                    Logger.Instance.Info("[TestFixtureSetup] PowerShell method execution", PSFixtureFileName);
                    PSUtils.ExecuteCmdlet(SetUpFixtureFunctionName, _psRunspace);
                }
                _psRunspace.Close();
            }
        }

        [SetUp]
        public void SetUp()
        {
            Logger.Instance.Info("SetUp", PSFixtureFileName);
            _psRunspace = PSUtils.PSRunspaceSetup(_psFixtureFilePath);
            if (_setUpFunctionExists)
            {
                Logger.Instance.Info("[Setup] PowerShell method execution", PSFixtureFileName);
                PSUtils.ExecuteCmdlet(SetUpFunctionName, _psRunspace);
            }
        }

        [Test]
        [Sequential]
        [TestCaseSource("PSTestList")]
        public void ExecutePSTest(string functionToTest)
        {
            Logger.Instance.Info("[ExecutePSTest] PowerShell method execution of " + functionToTest, PSFixtureFileName);
            PSUtils.ExecuteCmdlet(functionToTest, _psRunspace);
        }

        [TearDown]
        public void TearDown()
        {
            try
            {
                Logger.Instance.Info("TearDown", PSFixtureFileName);
                if (_tearDownFunctionExists)
                {
                    Logger.Instance.Info("[TearDown] PowerShell method execution", PSFixtureFileName);
                    PSUtils.ExecuteCmdlet(TearDownFunctionName, _psRunspace);
                }

            }
            finally
            {
                _psRunspace.Close();
                _psRunspace.Dispose();
                _psRunspace = null;
            }
        }

        [TestFixtureTearDown]
        public void TestFixtureTearDown()
        {
            Logger.Instance.Info("TestFixtureTearDown", PSFixtureFileName);
            using (_psRunspace = PSUtils.PSRunspaceSetup(_psFixtureFilePath))
            {
                if (_tearDownFixtureFunctionExists)
                {
                    Logger.Instance.Info("[TestFixtureTearDown] PowerShell method execution", PSFixtureFileName);
                    PSUtils.ExecuteCmdlet(TearDownFixtureFunctionName, _psRunspace);
                }

                _psRunspace.Close();
            }
            Logger.Instance.AddEmptyLine();
        }

        #endregion
    }
}
    